package com.otp.webapp.action;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.appfuse.model.LabelValue;
import org.appfuse.model.Organization;
import org.appfuse.model.User;
import org.appfuse.service.impl.UserManagerImpl;
import org.appfuse.util.ConvertUtil;
import org.springframework.beans.factory.annotation.Qualifier;

import com.otp.model.test.instance.TestInstance;
import com.otp.model.test.instance.TestInstanceScore;
import com.otp.model.test.instance.TestItemResponseInstance;
import com.otp.model.test.template.Item;
import com.otp.model.test.template.ResponseScoreOption;
import com.otp.model.test.template.ResponseScoreRange;
import com.otp.model.test.template.TestTemplate;
import com.otp.service.instance.ItemManager;
import com.otp.service.instance.TestInstanceManager;
import com.otp.webapp.comparators.CompItemsByObjectId;
import com.otp.webapp.comparators.CompTestItemResponseInstancesByItemId;
import com.otp.webapp.comparators.CompTestTemplatesByObjectId;

public class AdminTestInstanceList extends BasePage implements Serializable {

	private TestInstanceManager testInstanceManager;
	private ItemManager itemManager;
	private String userId;

	private String firstName;
	private String lastName;
	private String email;
	private String chooseTemplate;
	private String chooseOrganization;
	private Map<String,String> availableTemplate;
	private Map<String,String> availableOrgs;
	List<TestInstance> candidateTestInstance;
	private static final long serialVersionUID = -6143415000723353764L;

	public String getChooseTemplate() {
		return chooseTemplate;
	}

	public void setChooseTemplate(String chooseTemplate) {
		this.chooseTemplate = chooseTemplate;
	}	    

	public String getChooseOrganization() {
		return chooseOrganization;
	}

	public void setChooseOrganization(String chooseOrganization) {
		this.chooseOrganization = chooseOrganization;
	}

	public Map<String, String> getAvailableTemplate() {
		if(availableTemplate==null){
			List<TestTemplate> templateList= testInstanceManager.getTemplateByOrganization(getLoggedUserOrganization().getObjectId()+"");
			Set<TestTemplate> permittedList = getLoggedUser().getOrgs().getTestTemplates();
			List<LabelValue> temps = new ArrayList<LabelValue>();
			temps.add(new LabelValue("All Templates",""));
			if(templateList!=null) 
				for(TestTemplate t1:templateList){
					if(isSuperAdminUser()) temps.add(new LabelValue(t1.getName(), t1.getName()));
					else{
						for(TestTemplate t2:permittedList){
							if(t1.getObjectId()==t2.getObjectId()){
								temps.add(new LabelValue(t1.getName(), t1.getName()));
								break;
							}
						}
					}
				}
			availableTemplate = ConvertUtil.convertListToMap(temps);
		}
		return availableTemplate;
	}

	public Map<String, String> getAvailableOrgs() {
		if(availableOrgs==null){
			List<Organization> orgs = testInstanceManager.getAllOrganizations();
			List<LabelValue> temps = new ArrayList<LabelValue>();
			temps.add(new LabelValue("All Organizations",""));
			if(orgs!=null){
				for(Organization o:orgs){
					temps.add(new LabelValue(o.getOrganizationName(), o.getOrganizationName()));
				}
			}
			availableOrgs = ConvertUtil.convertListToMap(temps);
		}
		return availableOrgs;
	}


	public String getFirstName() {
		if(firstName==null) return ""; 
		else return firstName.trim();
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		if(lastName==null) return "";
		else return lastName.trim();
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		if(email==null) return "";
		else return email.trim();
	}
	public void setEmail(String email) {
		this.email = email;
	}	
	public List<TestInstance> getCandidateTestInstance() {
		return candidateTestInstance;
	}	
	public boolean isShowAssembleButton(){
		if(candidateTestInstance==null||candidateTestInstance.isEmpty()) return false;		
		if(chooseTemplate==null||chooseTemplate.isEmpty()) return false;
		return true;		
	}
	public boolean isShowAssembleWarning(){
		if(candidateTestInstance==null||candidateTestInstance.isEmpty()) return false;
		if(chooseTemplate!=null&&!chooseTemplate.isEmpty()) return false;
		return true;
	}

	//@Autowired
	public void setTestInstanceManager(@Qualifier("testInstanceManager")TestInstanceManager testInstancesManager) {
		this.testInstanceManager = testInstancesManager;    
	}

	//@Autowired
	public void setItemManager(@Qualifier("itemManager")ItemManager itemManager) {
		this.itemManager = itemManager;    
	}

	public String getUserId() {
		if(userId==null){
			HttpServletRequest request = getRequest();
			if(userManager==null){
				System.out.println("User manager is null!!!");
				userManager=new UserManagerImpl();
			}
			User user = userManager.getUserByUsername(request.getRemoteUser());
			this.userId=user.getId().toString();
		}		
		return this.userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}
	/**
	 * 
	 */

	public AdminTestInstanceList() {
		setSortColumn("objectId");
	}

	@SuppressWarnings("unchecked")
	public List<TestInstance> getTestInstances() {
		if(testInstanceManager==null){
			System.out.println("test instance manager is null");
		}
		return sort(testInstanceManager.getTestInstanceByUser(getUserId(),null));   
	}

	public List<TestInstance> getAllTestInstances(){
		List<TestInstance> returnedInstances = null;
		if(testInstanceManager!=null){
			returnedInstances = testInstanceManager.getAll();
		}
		return returnedInstances;
	}

	public String searchCandidateInstance(){

		candidateTestInstance = new ArrayList<TestInstance>();
		List<TestInstance> shortList; //User Criteria not applied yet

		String templateId=null;
		TestTemplate testTemplate=null;
		if(chooseTemplate!=null&&!chooseTemplate.isEmpty()) testTemplate =testInstanceManager.fetchTemplateByName(chooseTemplate);
		if(testTemplate!=null) templateId=testTemplate.getObjectId()+"";
		if(templateId==null) templateId="";

		String orgId=null;
		if(!isSuperAdminUser()) orgId=getLoggedUserOrganization().getObjectId()+"";
		else{
			Organization org = null;
			if(chooseOrganization!=null&&!chooseOrganization.isEmpty()) org = testInstanceManager.getOrgByName(chooseOrganization);
			if(org!=null) orgId=org.getObjectId()+"";
		}
		if(orgId==null) orgId="";


		if(orgId.isEmpty()){
			if(templateId.isEmpty()) shortList=testInstanceManager.getAllTestInstances();
			else 					 shortList=testInstanceManager.getTestInstancesByTemplate(templateId);
		}else{
			if(templateId.isEmpty()) shortList=testInstanceManager.getTestInstancesByOrganization(orgId);
			else 					 shortList=testInstanceManager.getTestInstancesByTemplateAndOrganization(templateId, orgId);
		}

		if(getFirstName().isEmpty()&&getLastName().isEmpty()) candidateTestInstance.addAll(shortList);
		else for(TestInstance ti:shortList) {
			if(ti.getAppUser().getFirstName()==null||ti.getAppUser().getLastName()==null) continue;
			if(ti.getAppUser().getFirstName().equalsIgnoreCase(getFirstName())
					||ti.getAppUser().getLastName().equalsIgnoreCase(getLastName())) candidateTestInstance.add(ti);
		}

		if(getEmail()!=null&&!getEmail().isEmpty())//TODO Not very efficient!!!  Email returns very few test instances Should search for this first
			for(int i=0; i<candidateTestInstance.size(); i++)
				if(!candidateTestInstance.get(i).getAppUser().getEmail().equals(getEmail()))
					candidateTestInstance.remove(i--); //Need to decrement this b/c item is removed; next item is i, not i+1 

		return null;
	}

	public String assembleData(){

		FacesContext facesContext = FacesContext.getCurrentInstance();
		ExternalContext externalContext = facesContext.getExternalContext();
		HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

		response.reset();
		response.setContentType("text/plain");
		response.setHeader("Content-disposition", "attachment; filename=\"report.csv\""); 

		if(!chooseTemplate.isEmpty()){
			//Note: questions, and answers are sorted by item id, so there is a correspondence from one list to the other
			//This is crucial!

			List<TestInstance> tests = new ArrayList<TestInstance>();
			for(TestInstance test:candidateTestInstance) if(test.getTestInstanceStateDesc().equals("COMPLETED")) tests.add(test);						
			String templateId = tests.get(0).getTestTemplate().getObjectId()+"";

			List<TestTemplate> subtests = testInstanceManager.getSubTests(templateId);
			Collections.sort(subtests, new CompTestTemplatesByObjectId());
			List<List<Item>> questions = new ArrayList<List<Item>>(); //First index is testTemplate, second is ItemId;
			List<List<TestInstanceScore>> scores = new ArrayList<List<TestInstanceScore>>(); //First index is testIstance, second testTemplate
			List<List<List<TestItemResponseInstance>>> answers = new ArrayList<List<List<TestItemResponseInstance>>>();//First index is testInstance, second testTemplate, third ItemId

			for(TestTemplate subtest:subtests){
				List<Item> itemsInSubtest = itemManager.getTestMapEntryByTemplate(subtest.getObjectId()+"");
				Collections.sort(itemsInSubtest, new CompItemsByObjectId());
				questions.add(itemsInSubtest);
			}
			for(TestInstance test:tests){
				List<TestInstanceScore> scoresSingleTestInstance = new ArrayList<TestInstanceScore>();
				List<List<TestItemResponseInstance>> answersSingleTestInstance = new ArrayList<List<TestItemResponseInstance>>();
				String testId = test.getObjectId()+"";
				for(int i=0; i<subtests.size(); i++){    				
					TestTemplate subtest = subtests.get(i);
					templateId = subtest.getObjectId()+"";
					List<Item> itemsInSubtest = questions.get(i);
					List<TestItemResponseInstance> answersNotNullInSubtest = new ArrayList<TestItemResponseInstance>();
					List<TestItemResponseInstance> answersAllInSubtest = new ArrayList<TestItemResponseInstance>();
					scoresSingleTestInstance.add(testInstanceManager.getSingleTestInstanceScore(testId, templateId));
					answersNotNullInSubtest=testInstanceManager.getTestItemResponseInstancesByInstanceIdAndTemplateId(testId, templateId);
					Collections.sort(answersNotNullInSubtest, new CompTestItemResponseInstancesByItemId());
					for(Item item:itemsInSubtest){
						boolean foundAnswer = false;
						for(TestItemResponseInstance answerNotNull:answersNotNullInSubtest){
							if(item.getObjectId()==answerNotNull.getRelatedItem().getObjectId()){
								answersAllInSubtest.add(answerNotNull);
								answersNotNullInSubtest.remove(answerNotNull);
								foundAnswer=true;
								break;
							}
						}
						if(!foundAnswer){
							TestItemResponseInstance dummyResponseInstance = new TestItemResponseInstance();
							dummyResponseInstance.setRelatedItem(item);//Needs this for sorting
							answersAllInSubtest.add(dummyResponseInstance);
						}
					} 
					Collections.sort(answersAllInSubtest, new CompTestItemResponseInstancesByItemId());
					answersSingleTestInstance.add(answersAllInSubtest);
				}
				scores.add(scoresSingleTestInstance);
				answers.add(answersSingleTestInstance);
			}

			//Finished reading data from DB, building output
			char comma = ',';
			char quotes = '"';
			char space = ' ';
			String separate = ""+comma+space;
			String endLine = "\n";
			String s=quotes+"ID"+quotes+separate+quotes+"Ethnicity"+quotes+separate+quotes+"Other Demographics"+quotes+separate;

			for(int i=0; i<subtests.size(); i++){//i refers to subtests
				for(Item it:questions.get(i)) s+=quotes+it.getDescription()+quotes+separate;
				s+=quotes+subtests.get(i).getDescription()+quotes;
				if(i<subtests.size()-1) s+=separate;
				else s+=endLine;
			}


			for(int i=0; i<tests.size(); i++){
				s+=tests.get(i).getObjectId()+separate+quotes+"Ethnicity"+quotes+separate+quotes+"Other Demographics"+quotes+separate;
				for(int j=0; j<subtests.size(); j++){
					for(TestItemResponseInstance respInst:answers.get(i).get(j)){
						String itemScoreStr;
						if(respInst.getObjectId()==null){
							itemScoreStr = "";
						}
						else{
							ResponseScoreOption option = respInst.getRelatedResponseOption();
							ResponseScoreRange range = option.getResponseRange();
							Double itemScore = option.getScore();
							if(range.getObjectId()==10002) itemScore=6-itemScore;//NOTICE REVERSE SCORING
							itemScoreStr = itemScore+"";
						}
						s+=itemScoreStr+separate;
					}
					s+=scores.get(i).get(j).getRawScore()+separate;
				}
				s+=endLine;
			}

			//Write file to output
			byte[] bytes = s.getBytes();        

			BufferedInputStream input = null;
			BufferedOutputStream output = null;

			try {
				input = new BufferedInputStream(new ByteArrayInputStream(bytes));
				output = new BufferedOutputStream(response.getOutputStream());

				byte[] buffer = new byte[1024];
				for (int length; (length = input.read(buffer)) > 0;) {
					output.write(buffer, 0, length);
				}
			}catch(Exception e){
				e.printStackTrace();
			} finally {
				try{
					output.close();
					input.close();
				}
				catch(Exception e){e.printStackTrace();}
			}
			facesContext.responseComplete();
		}//Close if loop defining if template has been constrained by the user

		return null;

	}

	public String generateStatistics(){

		FacesContext facesContext = FacesContext.getCurrentInstance();
		ExternalContext externalContext = facesContext.getExternalContext();
		HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

		response.reset();
		response.setContentType("text/plain");
		response.setHeader("Content-disposition", "attachment; filename=\"stats.csv\""); 

		if(!chooseTemplate.isEmpty()){
			//Note: questions, and answers are sorted by item id, so there is a correspondence from one list to the other
			//This is crucial!
			List<TestInstance> tests = new ArrayList<TestInstance>();
			for(TestInstance test:candidateTestInstance) if(test.getTestInstanceStateDesc().equals("COMPLETED")) tests.add(test);						
			String templateId = tests.get(0).getTestTemplate().getObjectId()+"";

			List<TestTemplate> subtests = testInstanceManager.getSubTests(templateId);
			Collections.sort(subtests, new CompTestTemplatesByObjectId());
			List<List<TestInstanceScore>> scores = new ArrayList<List<TestInstanceScore>>(); //First index is testIstance, second testTemplate

			for(TestInstance test:tests){
				List<TestInstanceScore> scoresSingleTestInstance = new ArrayList<TestInstanceScore>();
				for(TestTemplate subtest:subtests) scoresSingleTestInstance.add(testInstanceManager.getSingleTestInstanceScore(test.getObjectId()+"", subtest.getObjectId()+""));
				scores.add(scoresSingleTestInstance);
			}

			boolean singleOrg=true;
			Organization org = tests.get(0).getAppUser().getOrgs();
			for(int i=1; i<tests.size(); i++) 
				if(tests.get(i).getAppUser().getOrgs().getObjectId()!=org.getObjectId()){
					singleOrg=false;
					break;
				}

			char comma = ',';
			char space = ' ';
			String separate = ""+comma+space;
			String endLine = "\n";

			String s="";
			s+="Overview of Scores"+endLine;
			s+="Test is "+tests.get(0).getTestTemplate().getDescription()+endLine;
			if(singleOrg) s+="Organization is "+org.getOrganizationName()+endLine;
			else s+="Users belong to different organizations"+endLine;
			s+=tests.size()+" tests completed"+endLine;
			s+=new Date() + endLine;

			int i;
			int j;
			s+="Subtest description"+separate;
			for(j=0; j<subtests.size(); j++) s+=subtests.get(j).getDescription()+separate;
			s+="User Id"+separate;
			s+="User Name"+separate;
			s+="Organization"+endLine;

			Double[] sums = new Double[subtests.size()];
			Double[] sumsSq = new Double[subtests.size()];
			for(i=0;i<sums.length;i++) {sums[i]=0.; sumsSq[i]=0.;}
			for(i=0; i<tests.size(); i++){
				s+="Test Instance is "+tests.get(i).getObjectId()+separate;
				for(j=0; j<subtests.size(); j++) {
					Double score = scores.get(i).get(j).getRawScore();
					s+=score+separate;
					sums[j]+=score;
					sumsSq[j]+=score*score;
				}
				User user=tests.get(i).getAppUser();
				s+=user.getId()+separate;
				s+=user.getFullName()+separate;
				s+=user.getOrgs().getOrganizationName()+endLine;
			}

			int N = tests.size();
			s+="mean value"+separate;
			for(j=0; j<subtests.size(); j++) s+=(sums[j]/N)+separate;
			s+=endLine;
			s+="standard deviation"+separate;
			for(j=0; j<subtests.size(); j++) s+=java.lang.Math.sqrt((sumsSq[j]-sums[j]*sums[j]/N)/(N-1))+separate;
			s+=endLine;

			//Write file to output
			byte[] bytes = s.getBytes();        

			BufferedInputStream input = null;
			BufferedOutputStream output = null;

			try {
				input = new BufferedInputStream(new ByteArrayInputStream(bytes));
				output = new BufferedOutputStream(response.getOutputStream());

				byte[] buffer = new byte[1024];
				for (int length; (length = input.read(buffer)) > 0;) {
					output.write(buffer, 0, length);
				}
			}catch(Exception e){
				e.printStackTrace();
			} finally {
				try{
					output.close();
					input.close();
				}
				catch(Exception e){e.printStackTrace();}
			}
			facesContext.responseComplete();
		}//Close if loop defining if template has been constrained by the user

		return null;

	}  
}
